﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;

namespace ChessEngine.Native
{
    

    public class NativeEngine : IEngine
    {
        private const string DLL = "NativeChessEngine.dll";

        private IntPtr _context = IntPtr.Zero;
        private Piece[] _squares = new Piece[64];
        private uint _flags = 0;
        private PieceColor _whoseMove = PieceColor.White;

        public NativeEngine(string blackStrategy = "", string whiteStrategy = "")
        {
            _context = Engine_StartDefaultGame(blackStrategy, whiteStrategy);

            UpdateState();
        }
        public NativeEngine(string fen, string blackStrategy = "", string whiteStrategy = "")
        {
            _context = Engine_StartGame(fen, blackStrategy, whiteStrategy);

            UpdateState();
        }
        ~NativeEngine()
        {
            Dispose(false);
        }

        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }
        private void Dispose(bool disposing)
        {
            Engine_Free(_context);
        }

        public bool IsWhiteMate
        {
            get
            {
                return Convert.ToBoolean(_flags & (uint)StateFlags.WHITE_MATE);
            }
        }
        public bool IsBlackMate
        {
            get
            {
                return Convert.ToBoolean(_flags & (uint)StateFlags.BLACK_MATE);
            }
        }
        public bool IsCheckmate
        {
            get
            {
                return IsWhiteMate || IsBlackMate;
            }
        }
        public bool IsStalemate
        {
            get
            {
                return Convert.ToBoolean(_flags & (uint)StateFlags.STALEMATE);
            }
        }
        public bool IsGameOver
        {
            get
            {
                return IsWhiteMate || IsBlackMate || IsStalemate;
            }
        }
        public PieceColor WhoseMove
        {
            get
            {
                return _whoseMove;
            }
        }

        public unsafe List<Move> GetMoves()
        {
            Move* pMoves = null;
            int numMoves;

            try
            {
                Engine_GetMoves(_context, out pMoves, out numMoves);

                List<Move> ret = new List<Move>();

                for (Move* iter = pMoves, end = pMoves + numMoves; iter != end; ++iter)
                    ret.Add(*iter);

                return ret;
            }
            finally
            {
                Engine_FreeMoves(pMoves);
            }
        }
        public void DoMove(Move m)
        {
            Engine_DoMove(_context, m);

            UpdateState();
        }
        public Move DoAIMove()
        {
            Move m = Engine_AIMove(_context);

            //UpdateState();

            return m;
        }
        public Task<Move> DoAIMoveAsync()
        {
            return Task.Factory.StartNew(() => DoAIMove());
        }

        public Piece GetPieceAt(int col, int row)
        {
            return _squares[row * 8 + col];
        }
        public PieceType GetPieceTypeAt(int col, int row)
        {
            return GetPieceAt(col, row).Type;
        }
        public PieceColor GetPieceColorAt(int col, int row)
        {
            return GetPieceAt(col, row).Color;
        }

        private unsafe void UpdateState()
        {
            fixed (Piece* squares = _squares)
            {
                Engine_GetBoardState(_context, squares, out _flags, out _whoseMove);
            }
        }
        

        [DllImport(DLL)]
        private static extern IntPtr Engine_StartGame(string fen, string blackStrategy, string whiteStrategy);
        [DllImport(DLL)]
        private static extern IntPtr Engine_StartDefaultGame(string blackStrategy, string whiteStrategy);
        [DllImport(DLL)]
        private static extern void Engine_SetStrategies(IntPtr ctx, string blackStrategy, string whiteStrategy);
        [DllImport(DLL)]
        private static extern void Engine_DoMove(IntPtr ctx, Move m);
        [DllImport(DLL)]
        private static extern Move Engine_AIMove(IntPtr ctx);
        [DllImport(DLL)]
        private static unsafe extern void Engine_GetMoves(IntPtr ctx, out Move* moves, out int numMoves);
        [DllImport(DLL)]
        private static unsafe extern void Engine_FreeMoves(Move* moves);
        [DllImport(DLL)]
        private static unsafe extern void Engine_GetBoardState(IntPtr ctx, Piece* squares, out uint flags, out PieceColor whoseMove);
        [DllImport(DLL)]
        private static extern void Engine_Free(IntPtr ctx);
    }
}
